﻿/**	@file		archive.hpp
 *	@date		(2009-01-25 14:04:49)/(2012-03-10 12:23:29)
 *-----------------------------------------------------------------------------
 *	@version	1.0.7.14
 *	@author		Nick Shallery	(nicknide@gmail.com)
 *	@copyright	YOU HAVE ALL LEFT WITH THIS CODE WHILE YOU KEEP COMMENT IN EVERY COPY.
 *-----------------------------------------------------------------------------
**/

#if			!defined(__LOLIX_TOY__ARCHIVE__NICK__K_IN_G__)
#define		__LOLIX_TOY__ARCHIVE__NICK__K_IN_G__
//==============================================================================
//--------------------------------------------------------------------------

#if			!defined(__LOLIX__LOLI__FILE__H__K_IN_G__)
	#include	"../loli/file.h"
#endif	//	!defined(__LOLIX__LOLI__FILE__H__K_IN_G__)

namespace lolix{namespace toy{
	template<typename _Teh>
	class	archive;
	class	archive_error_handle_no_throw;
	class	archive_error_handle_with_throw;
	typedef	archive<archive_error_handle_no_throw>	archive_no_throw;
}}

namespace lolix{namespace toy{namespace _help{
	class TExit{};
	typedef void (TExit::*mem_call)();

	struct func;
	struct func_list
	{
		func* fc;
		LOLIX_INLINE_CALL func_list(void):fc(0){}
		LOLIX_INLINE_CALL void invoke();
	};

	struct func
	{
		template<typename _Ty, typename _Tr>
		LOLIX_INLINE_CALL func(func_list* flist, _Ty* ptr, _Tr (_Ty::*func)(void)):fc_list(flist), usr_ptr((TExit*)ptr), call((mem_call)func)
		{
			next = fc_list->fc;
			fc_list->fc = this;
		}
		LOLIX_INLINE_CALL func(func_list* flist, void* ptr, mem_call c):fc_list(fc_list), usr_ptr((TExit*)ptr), call(c)
		{
			next = fc_list->fc;
			fc_list->fc = this;
		}
		LOLIX_INLINE_CALL ~func(void){fc_list->fc = fc_list->fc->next;}
		LOLIX_INLINE_CALL void invoke(){(usr_ptr->*call)();}
		func_list* fc_list;
		func* next;
		mem_call call;
		TExit* usr_ptr;
#if			LOLIX_CFG__IS_DEBUG
	private:
		void* operator new(size_t);
		void operator delete(void*);
#endif	//	LOLIX_CFG__IS_DEBUG
	};

	LOLIX_INLINE_CALL void func_list::invoke(void)
	{
		while ( fc )
		{
			func* next = fc->next;
			fc->invoke();
			fc = next;
		}
	}

	template<typename _Ty>
	struct func_destroy
		: public func
	{
		LOLIX_INLINE_CALL func_destroy(func_list* fc_list, _Ty* ptr): func(fc_list, ptr, (mem_call)(_Ty::&~_Ty)){}
	};
}}}


template<typename _Teh>
class lolix::toy::archive
{
	typedef		archive<_Teh>	this_type;
public:
	typedef	loli::file_flow file_type;
	typedef	_Teh		error_handle_type;
	typedef	void		property_type;

	LOLIX_INLINE_CALL	explicit archive(file_type* fs): _udat(0), _ff(fs), _arc_flag(0)
	{ _ff->inc_ref(); };
	LOLIX_INLINE_CALL	archive(file_type* fs, const _Teh& eh): _udat(0), _ff(fs), _eh(eh), _arc_flag(0)
	{ _ff->inc_ref(); };

	LOLIX_INLINE_CALL	archive(const archive& other)
		:  _udat(other._udat)
		, _ff(other._ff)
		, _eh(other._eh)
		, _arc_flag(other._arc_flag)
	{
		_ff->inc_ref();
	}

	LOLIX_INLINE_CALL	~archive(void)
	{
		_ff->release();
	}

	LOLIX_INLINE_CALL	archive& operator = (const archive& other)
	{
		_udat = other._udat;
		_ff->release();
		(_ff = other._ff)->inc_ref();
		_eh = other._eh;
		_arc_flag = other._arg_flag;
		return *this;
	}

	LOLIX_INLINE_CALL	error_handle_type&	get_eh(void)
	{
		return _eh;
	}

	LOLIX_INLINE_CALL	const error_handle_type&	get_eh(void)const
	{
		return _eh;
	}

	LOLIX_INLINE_CALL	file_type*	get_fh(void)
	{
		return _ff;
	}

	LOLIX_INLINE_CALL	file_type const*	get_fh(void)const
	{
		return _ff;
	}

	LOLIX_INLINE_CALL	this_type&	touch(void* buf, size_type buf_size)
	{
		if ( !_eh )
			return *this;

		LOLIX_RETNV rtv;
		const size_type query_size = buf_size;
		if ( LOLIX_IS_ERROR(rtv = _ff->touch(buf, &buf_size)) || query_size != buf_size )
			_eh.handle_touch_error(_ff, query_size, buf_size, rtv);
		return *this;
	}

	LOLIX_INLINE_CALL	this_type&	read(void* buf, size_type buf_size)
	{
		if ( !_eh )
			return *this;

		LOLIX_RETNV rtv;
		const size_type query_size = buf_size;
		if ( LOLIX_IS_ERROR(rtv = _ff->read(buf, &buf_size)) || query_size != buf_size )
			_eh.handle_read_error(_ff, query_size, buf_size, rtv);
		return *this;
	}

	LOLIX_INLINE_CALL	this_type&	write(const void* buf, size_type buf_size)
	{
		if ( !_eh )
			return *this;

		LOLIX_RETNV rtv;
		const size_type query_size = buf_size;
		if ( LOLIX_IS_ERROR(rtv = _ff->write(buf, &buf_size)) || query_size != buf_size )
			_eh.handle_write_error(_ff, query_size, buf_size, rtv);
		return *this;
	}

	lolix::LOLIX_RETNV write_flag(void)const
	{
		lolix::LOLIX_RETNV rtv;
		size_type write_size = sizeof _arc_flag;
		if ( LOLIX_CHECK_ERROR(rtv = _ff->write(&_arc_flag, &write_size)) )
			return rtv;
		if ( write_size != sizeof _arc_flag )
			return lolix::LOLIX_ERROR_LESS_RESOURCE;
		return rtv;
	}

	lolix::LOLIX_RETNV read_flag(void)
	{
		lolix::LOLIX_RETNV rtv;
		size_type read_size = sizeof _arc_flag;
		if ( LOLIX_CHECK_ERROR(rtv = _ff->read(&_arc_flag, &read_size)) )
			return rtv;
		if ( read_size != sizeof _arc_flag )
			return lolix::LOLIX_ERROR_LESS_RESOURCE;
		return rtv;
	}

	LOLIX_INLINE_CALL bool is_pack_value(void)const
	{
		return _is_pack_int?true:false;
	}

	LOLIX_INLINE_CALL void set_pack_value(bool set)
	{
		_is_pack_int = set?1:0;
	}

	LOLIX_INLINE_CALL void swap(this_type& o)
	{
		lolix::swap(_udat, o._udat);
		lolix::swap(_ff, o._ff);
		lolix::swap(_eh, o._eh);
		lolix::swap(_arc_flag, o._arc_flag);
	}

	LOLIX_INLINE_CALL void* get_udat(void)const
		{
		return _udat;
		}

	template<typename _Ty>
	LOLIX_INLINE_CALL _Ty get_udat(void)const
		{
		return (_Ty)_udat;
		}


	LOLIX_INLINE_CALL void set_udat(void* udat)
		{
		_udat = udat;
		}

	template<typename _Ty>
	LOLIX_INLINE_CALL void set_udat(_Ty udat)
		{
		_udat = (void*)udat;
		}
private:
	void*		_udat;
	file_type*	_ff;
	_Teh		_eh;
	__BEGIN_DISABLE_NO_NAME_STRUCT_WARNING()
	union
	{
		lx_u32	_arc_flag;
		struct
		{
		lx_u32	_is_pack_int	: 1;
		};
	};
	__END_DISABLE_NO_NAME_STRUCT_WARNING()
};


class lolix::toy::archive_error_handle_no_throw
{
	typedef archive_error_handle_no_throw this_type;
	LOLIX_RETNV	_err;
	struct	_bool_struct{};
	typedef	loli::file_flow	file_type;
	_help::func_list _func_list;
	template<typename _Ty>
	struct _cp_func_member
		: public _help::func
	{
		LOLIX_INLINE_CALL _cp_func_member(_help::func_list* fc_list, _Ty* ptr, void (_Ty::*proc)(void)): func(fc_list, ptr, proc){}
		LOLIX_INLINE_CALL _cp_func_member(_cp_func_member const& o):func(o){this->fc.fc = this;}
		LOLIX_INLINE_CALL _cp_func_member(_cp_func_member && o):func(o){this->fc.fc = this;}
	};

	template<typename _Ty>
	struct _cp_func_destroy
		: public _cp_func_member<_Ty>
	{
		LOLIX_INLINE_CALL _cp_func_destroy(_help::func_list* fc_list, _Ty* ptr): func_destroy(fc_list, ptr, &_Ty::~_Ty){}
		LOLIX_INLINE_CALL _cp_func_destroy(_cp_func_destroy const& o):_cp_func_member(o){}
		LOLIX_INLINE_CALL _cp_func_destroy(_cp_func_destroy && o):_cp_func_member(o){}
	};

public:
	LOLIX_INLINE_CALL archive_error_handle_no_throw(void)
		: _err(LOLIX_RETNV_OK){}

	LOLIX_INLINE_CALL LOLIX_RETNV	err(void)const
	{
		return _err;
	}

	template<typename _Ty>
	LOLIX_INLINE_CALL _cp_func_member<_Ty> push_member(_Ty* ptr, void (_Ty::*proc)(void))
	{
		return _cp_func_member<_Ty>(&_func_list, ptr, proc);
	}

	template<typename _Ty>
	LOLIX_INLINE_CALL auto push_destroy(_Ty* ptr) -> decltype(__Push_destroy(ptr))
	{
		return __Push_destroy(ptr, 0);
	}

	template<typename _Ty>
	LOLIX_INLINE_CALL _cp_func_destroy<_Ty> __Push_destroy(_Ty* ptr, decltype(&_Ty::~_Ty))
	{
		return _cp_func_destroy<_Ty>(&_func_list, ptr);
	}

	template<typename _Ty>
	LOLIX_INLINE_CALL int __Push_destroy(_Ty* ptr, ...)
	{
		return 0;
		//return _cp_func_destroy<_Ty>(&_func_list, ptr);
	}

	//template<typename _Ty>
	//LOLIX_INLINE_CALL _cp_func_destroy<_Ty> push_destroy(_Ty* ptr)
	//{
	//	return _cp_func_destroy<_Ty>(&_func_list, ptr);
	//}

	LOLIX_INLINE_CALL _help::func_list* flist(void)
	{
		return &_func_list;
	}

	LOLIX_INLINE_CALL operator _bool_struct* (void)const
	{
		return LOLIX_IS_ERROR(_err)?(_bool_struct*)0:(_bool_struct*)1;
	}

	LOLIX_INLINE_CALL void	handle_touch_error(file_type*, size_type /*query_size*/, size_type /*finish_size*/, LOLIX_RETNV rtv)
	{
		_err = LOLIX_IS_ERROR(rtv)?rtv:LOLIX_ERROR_FILE;
		_func_list.invoke();
	}
	LOLIX_INLINE_CALL void	handle_read_error(file_type*, size_type /*query_size*/, size_type /*finish_size*/, LOLIX_RETNV rtv)
	{
		_err = LOLIX_IS_ERROR(rtv)?rtv:LOLIX_ERROR_FILE;
		_func_list.invoke();
	}
	LOLIX_INLINE_CALL void	handle_write_error(file_type*, size_type /*query_size*/, size_type /*finish_size*/, LOLIX_RETNV rtv)
	{
		_err = LOLIX_IS_ERROR(rtv)?rtv:LOLIX_ERROR_FILE;
		_func_list.invoke();
	}
	LOLIX_INLINE_CALL void swap(this_type& o)
	{
		lolix::swap(_err, o._err);
		lolix::swap(_func_list, o._func_list);
	}
};

#if			LOLIX_CFG__THROWS_EXCEPT
class lolix::toy::archive_error_handle_with_throw
{
	typedef archive_error_handle_with_throw this_type;
	LOLIX_RETNV	_err;
	struct	_bool_struct{};
	typedef	loli::file_flow	file_type;
public:
	LOLIX_INLINE_CALL archive_error_handle_with_throw(void)
		: _err(LOLIX_RETNV_OK){}

	LOLIX_INLINE_CALL LOLIX_RETNV	err(void)const
	{
		return _err;
	}

	LOLIX_INLINE_CALL operator _bool_struct* (void)const
	{
		return LOLIX_IS_ERROR(_err)?(_bool_struct*)0:(_bool_struct*)1;
	}

	LOLIX_INLINE_CALL void	handle_touch_error(file_type*, size_type /*query_size*/, size_type /*finish_size*/, LOLIX_RETNV rtv)
	{
		throw rtv;
	}
	LOLIX_INLINE_CALL void	handle_read_error(file_type*, size_type /*query_size*/, size_type /*finish_size*/, LOLIX_RETNV rtv)
	{
		throw rtv;
	}
	LOLIX_INLINE_CALL void	handle_write_error(file_type*, size_type /*query_size*/, size_type /*finish_size*/, LOLIX_RETNV rtv)
	{
		throw rtv;
	}
	LOLIX_INLINE_CALL void swap(this_type& o)
	{
		lolix::swap(_err, o._err);
	}
};

#endif	//	LOLIX_CFG__THROWS_EXCEPT

namespace lolix{
template<typename _Teh>
	LOLIX_INLINE_CALL void swap(toy::archive<_Teh>& l, toy::archive<_Teh>& r)
	{
		l.swap(r);
	}

	LOLIX_INLINE_CALL void swap(toy::archive_error_handle_no_throw& l, toy::archive_error_handle_no_throw& r)
	{
		l.swap(r);
	}
}


//--------------------------------------------------------------------------
//==============================================================================
#endif	//	!defined(__LOLIX_TOY__ARCHIVE__NICK__K_IN_G__)
